﻿using System;
using System.ComponentModel;
using System.Linq.Expressions;

namespace Integrity
{
  public class OneToOne<TOwner, TValue> : IReferenceProp<TOwner, TValue>
    where TValue : class
    where TOwner : class
  {
    private readonly Func<TOwner, TValue> _storageGetter;
    private readonly Action<TOwner, TValue> _storageSetter;
    private readonly Func<OneToOne<TValue, TOwner>> _corrProp;
    public OneToOne<TValue, TOwner> CorrProp { get { return _corrProp(); } }
    public bool CascadeDeletion { get; private set; }
    private PropertyChangedEventArgs Args { get; set; }

    public void Reset(TOwner owner)
    {
      SetValue(owner, null);
    }

    bool IProperty.IsCollection { get { return false; } }
    RelationKind IProperty.Relation { get { return RelationKind.OneToOne; } }
    object IProperty.GetValue(object owner) { return GetValue((TOwner)owner); }
    void IReferenceProp.SetValue(object owner, object value) { SetValue((TOwner)owner, (TValue)value); }
    void IProperty.Reset(object owner) { Reset((TOwner)owner); }
    IProperty IProperty.CorrProp { get { return CorrProp; } }

    public OneToOne(Expression<Func<TOwner, TValue>> propertyExpression, Func<TOwner, TValue> storageGetter, Action<TOwner, TValue> storageSetter, Func<OneToOne<TValue, TOwner>> corrProp, bool cascadeDeletion)
    {
      Integrity.Register<TOwner>(this);
      CascadeDeletion = cascadeDeletion;
      Args = ObservableHelper.CreateArgs(propertyExpression);
      _storageGetter = storageGetter;
      _storageSetter = storageSetter;
      _corrProp = corrProp;
    }

    public TValue GetValue(TOwner owner)
    {
      return _storageGetter(owner);
    }

    public void SetValue(TOwner owner, TValue value)
    {
      var oldValue = _storageGetter(owner);
      if (oldValue == value) return;
      _storageSetter(owner, value);

      var ifc = owner as IIntegrityObject;
      if (ifc != null) ifc.OnPropertyChanged(Args);

      var corrProp = CorrProp;
      if (oldValue != null)
      {
        if (CascadeDeletion)
          Integrity.Release(oldValue);
        else
          corrProp.SetValue(oldValue, null);
      }
      if (value != null)
      {
        corrProp.SetValue(value, owner);
      }
    }
  }
}